package com.uwlaser.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.uwlaser.domain.*;
import com.uwlaser.service.CraftService;
import com.uwlaser.service.dto.*;
import com.uwlaser.service.mapper.*;
import com.uwlaser.utils.StringUtils;
import com.uwlaser.utils.enums.DictValueEnum;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author zk-Qin
* @date 2022-05-11
*/
@Service
@AllArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class CraftServiceImpl implements CraftService {

    //工艺参数
    private final CraftParamMapper craftParamMapper;

    //工艺参数详情
    private final CraftParamDetailMapper craftParamDetailMapper;

    //工艺工步
    private final CraftStepMapper craftStepMapper;

    //工步详情
    private final CraftStepDetailMapper craftStepDetailMapper;

    //产品型号
    private final ProductModelMapper productModelMapper;

    //扫码规则
    private final CraftScanRuleMapper craftScanRuleMapper;

    //物料信息
    private final DataMaterialSingleMapper dataMaterialSingleMapper;

    //数据表
    private final DataMainMapper dataMainMapper;

    //过站数据
    private final DataPassStationMapper dataPassStationMapper;

    //工艺路线
    private final DataProductPathMapper dataProductPathMapper;

    //程序号
    private final CraftProductProgramMapper craftProductProgramMapper;

    /**
     * 获取配方信息
     * @param requestParam
     * @return
     */
    @Override
    public GetRecipeInfoResult getRecipeInfo(RequestParam<GetRecipeInfoParam> requestParam) {
        //请求参数
        GetRecipeInfoParam param = requestParam.getParam();
        //返回对象
        GetRecipeInfoResult result = new GetRecipeInfoResult();

        ProductModel productModel = getProductModel(param.getProductCode());

        //获取配方信息
        List<CraftParamDetail> paramList = getCraftParamDetail(param.getProductCode(),requestParam.getCraftOperation().getOperationCode());
        if(paramList != null && paramList.size() > 0){
            for (CraftParamDetail paramDetail : paramList) {
                result.addParam(
                        paramDetail.getParamCode(),
                        paramDetail.getParamName(),
                        paramDetail.getParamLower(),
                        paramDetail.getParamUpper(),
                        paramDetail.getParamUnit(),
                        paramDetail.getStandard(),
                        paramDetail.getIsNecessary()
                );
            }
        }
        //获取工步信息
        List<CraftStepDetail> stepList = getCraftStepDetail(param.getProductCode(), requestParam.getCraftOperation().getOperationCode());
        if(stepList  != null && stepList.size() > 0){
            for (CraftStepDetail stepDetail : stepList) {
                result.addStep(
                        stepDetail.getStepNo(),
                        stepDetail.getStepCode(),
                        stepDetail.getStepName(),
                        stepDetail.getStepType(),
                        stepDetail.getStepInstructions(),
                        stepDetail.getMaterialCode(),
                        stepDetail.getMaterialName(),
                        stepDetail.getIsCheck(),
                        stepDetail.getProgram(),
                        stepDetail.getSleeve(),
                        stepDetail.getTool()
                );
            }
        }
        //获取程序号信息
        CraftProductProgram program = getProductProgram(param.getProductCode(),requestParam.getCraftOperation().getOperationCode());
        result.setProgramNo(program.getProgramOn());
        result.setOperationCode(requestParam.getCraftOperation().getOperationCode());
        result.setOperationName(requestParam.getCraftOperation().getOperationName());
        result.setProductCode(productModel.getProductCode());
        result.setProductName(productModel.getProductName());
        result.setProductType(productModel.getProductType());
        if(result.getParams() == null){
            result.setParams(new ArrayList<>());
        }
        if(result.getSteps() == null){
            result.setSteps(new ArrayList<>());
        }
        return result;
    }


    /**
     * 扫码校验
     * @param requestParam
     * @return
     */
    @Override
    public ScanCodeCheckResult scanCodeCheck(RequestParam<ScanCodeCheckParam> requestParam) {
        //请求参数
        ScanCodeCheckParam param = requestParam.getParam();
        //返回结果
        ScanCodeCheckResult result = new ScanCodeCheckResult();

        //根据编码规则校验编码
        CraftStepDetail craftStepDetail = checkCode(requestParam);

        //如果是单件码进入判断
        if(DictValueEnum.STEP_TYPE_SINGLE.getValue().equals(craftStepDetail.getStepType())){
            //校验单体码是否重复绑定
            checkSingleCodeRepeat(requestParam);

            //如果是电芯或者模组（校验是否是合格的）
            checkDataMain(requestParam,craftStepDetail);
        }
        result.setMaterialName(craftStepDetail.getMaterialName());
        result.setMaterialCode(craftStepDetail.getMaterialCode());
        result.setQrCode(param.getQrCode());
        return result;
    }

    /**
     * 扫码校验-多个
     * @param requestParam
     * @return
     */
    @Override
    public List<ScanCodeCheckBatchResult> scanCodeCheckBatch(RequestParam<ScanCodeCheckBatchParam> requestParam) {
        //请求参数
        ScanCodeCheckBatchParam param = requestParam.getParam();
        //返回结果
        List<ScanCodeCheckBatchResult> result = new ArrayList<>();

        List<ScanCodeCheckBatchParam.QrCode> qrCodes = param.getQrCodes();

        for (ScanCodeCheckBatchParam.QrCode qrCode : qrCodes) {
            ScanCodeCheckParam scanCodeCheckParam = new ScanCodeCheckParam();
            scanCodeCheckParam.setProductCode(param.getProductCode());
            scanCodeCheckParam.setQrCode(qrCode.getQrCode());
            scanCodeCheckParam.setStepCode(qrCode.getStepCode());
            scanCodeCheckParam.setDeviceCode(param.getDeviceCode());
            scanCodeCheckParam.setOperator(param.getOperator());
            scanCodeCheckParam.setTimestamp(param.getTimestamp());

            RequestParam<ScanCodeCheckParam> scanCodeParam = new RequestParam<>();
            scanCodeParam.setParam(scanCodeCheckParam);
            scanCodeParam.setDeviceMain(requestParam.getDeviceMain());
            scanCodeParam.setFactoryLine(requestParam.getFactoryLine());
            scanCodeParam.setFactorySection(requestParam.getFactorySection());
            scanCodeParam.setFactoryStation(requestParam.getFactoryStation());
            scanCodeParam.setCraftOperation(requestParam.getCraftOperation());

            ScanCodeCheckBatchResult res = new ScanCodeCheckBatchResult();

            try {
                //调用单个校验方法
                ScanCodeCheckResult scanCodeCheckResult = scanCodeCheck(scanCodeParam);
                res.setMessage("校验通过");
                res.setStatus(HttpStatus.OK.value());
                res.setQrCode(scanCodeCheckResult.getQrCode());
                res.setMaterialCode(scanCodeCheckResult.getMaterialCode());
                res.setMaterialName(scanCodeCheckResult.getMaterialName());
                result.add(res);
            } catch (Exception e){
                res.setMessage(StringUtils.isEmpty(e.getMessage()) ? "发生空指针异常！":e.getMessage());
                res.setStatus(HttpStatus.BAD_REQUEST.value());
                res.setQrCode(qrCode.getQrCode());
                result.add(res);
            }
        }
        return result;
    }

    /**
     * 校验当前条码状态是否是合格状态
     * @param requestParam
     * @param craftStepDetail
     */
    private void checkDataMain(RequestParam<ScanCodeCheckParam> requestParam,CraftStepDetail craftStepDetail) {

        //请求参数
        ScanCodeCheckParam param = requestParam.getParam();

        //如果工步没有绑定物料编码不判断
        if(StringUtils.isEmpty(craftStepDetail.getMaterialCode())){
            return;
        }
        //根据物料编码匹配产品型号
        ProductModel productModel = new ProductModel();
        productModel.setMaterialCode(craftStepDetail.getMaterialCode());
        ProductModel model = productModelMapper.selectOne(new QueryWrapper<>(productModel));

        //如果根据物料编码匹配到产品型号，查询主表信息
        if(model != null){
            DataMain dataMain = new DataMain();
            dataMain.setProductSn(param.getQrCode());
            dataMain.setProductCode(model.getProductCode());
            dataMain.setProductType(model.getProductType());
            dataMain.setLienCode(requestParam.getFactoryLine().getModelCode());
            // delete by zzj 2022年6月15日 【模组堆叠】扫码校验电芯，传的是模组的【设备编号】，那就查不到数据，所以要把工段条件注释掉。
//            dataMain.setSectionCode(requestParam.getFactorySection().getModelCode());
            DataMain main = dataMainMapper.selectOne(new QueryWrapper<>(dataMain));
            if(main == null){
                throw new RuntimeException("扫码校验失败，当前二维码【"+param.getQrCode()+"】匹配产品类型为【"+model.getProductCode()+"】在主列表中未发现，请检查后再试！");
            }

            //判断当前产品不是OK抛异常
            if(!DictValueEnum.RESULT_OK.getValue().equals(main.getResult())){
                throw new RuntimeException("扫码校验失败，当前二维码【"+param.getQrCode()+"】匹配产品型号为【"+model.getProductCode()+"】该产品属于不合格产品，不能进行绑定！");
            }
        }
    }

    /**
     * 校验单体码是否重复绑定
     * @param requestParam
     */
    private void checkSingleCodeRepeat(RequestParam<ScanCodeCheckParam> requestParam) {
        ScanCodeCheckParam param = requestParam.getParam();
        DataMaterialSingle dataMaterialSingle = new DataMaterialSingle();
        dataMaterialSingle.setLienCode(requestParam.getFactoryLine().getModelCode());
        dataMaterialSingle.setSectionCode(requestParam.getFactorySection().getModelCode());
        dataMaterialSingle.setStationCode(requestParam.getFactoryStation().getModelCode());
        dataMaterialSingle.setMaterialSn(param.getQrCode());
        Integer integer = dataMaterialSingleMapper.selectCount(new QueryWrapper<>(dataMaterialSingle));
        //判断有没有设置(返修|重测)标识
        if(integer > 0 && !checkCurrentRepeat(requestParam)){
           throw new RuntimeException("扫码校验失败，当前二维码【"+param.getQrCode()+"】已经绑定过了，单件码不能重复绑定，请解绑后再试！");
        }
    }

    /**
     * 校验当前请求是否携带返修标识
     * @param requestParam
     * @return
     */
    private boolean checkCurrentRepeat(RequestParam<ScanCodeCheckParam> requestParam) {
        //获取当前工序最新的进站记录
        DataPassStation dataPassStation = new DataPassStation();
        dataPassStation.setLienCode(requestParam.getFactoryLine().getModelCode());
        dataPassStation.setSectionCode(requestParam.getFactorySection().getModelCode());
        dataPassStation.setStationCode(requestParam.getFactoryStation().getModelCode());
        dataPassStation.setOperationCode(requestParam.getCraftOperation().getOperationCode());
        DataPassStation newPassStation = dataPassStationMapper.getNewPassStation(dataPassStation);
        if(newPassStation == null ){
            return false;
        }
        //判断进站时是否携带返修标识
        if(!newPassStation.getProcessedMode().equals(DictValueEnum.REPAIR_SIGN_REPAIR.getValue())){
            //判断是否在页面设置了返修模式
            DataProductPath dataProductPath = new DataProductPath();
            dataProductPath.setProductSn(newPassStation.getProductSn());
            dataProductPath.setOperationCode(requestParam.getCraftOperation().getOperationCode());
            DataProductPath path = dataProductPathMapper.selectOne(new QueryWrapper<>(dataProductPath));
            if(path == null || !path.getRepairSign().equals(DictValueEnum.REPAIR_SIGN_REPAIR.getValue())){
                return false;
            }
        }
        return true;
    }

    /**
     * 根据编码规则校验
     * @param requestParam
     * @return
     */
    private CraftStepDetail checkCode(RequestParam<ScanCodeCheckParam> requestParam) {

        //请求参数
        ScanCodeCheckParam param = requestParam.getParam();

        //匹配成功的工步信息
        CraftStepDetail craftStepDetail = null;

        //获取扫码规则
        if(!StringUtils.isEmpty(param.getStepCode())){
            //根据工步获取规则
            craftStepDetail = getCraftStepDetail(param.getProductCode(),requestParam.getCraftOperation().getOperationCode(),param.getStepCode());
            //根据规则规则校验
            boolean res = checkCraftScanRule(craftStepDetail, param.getQrCode());
            if(!res){
                throw new RuntimeException("扫码校验失败，当前二维码【"+param.getQrCode()+"】不符合MES校验规则！");
            }

        } else {
            //如果没有工步编号则匹配这个工序配置的工步
            List<CraftStepDetail> list = getCraftStepDetail(param.getProductCode(), requestParam.getCraftOperation().getOperationCode());
            for (CraftStepDetail stepDetail : list) {
                try {
                    //如果是（拧紧类型）或者（其他类型）的工步不校验
                    if(DictValueEnum.STEP_TYPE_SCREW.getValue().equals(stepDetail.getStepType())
                    || DictValueEnum.STEP_TYPE_OTHER.getValue().equals(stepDetail.getStepType())){
                        continue;
                    }
                    //循环匹配校验
                    boolean res = checkCraftScanRule(stepDetail, param.getQrCode());
                    if(res == true){
                        craftStepDetail = stepDetail;
                        break;
                    }
                } catch (Exception e){ }
            }
            //如果循环匹配后都没有匹配成功抛异常
            if(craftStepDetail == null){
                throw new RuntimeException("扫码校验失败，当前二维码【"+param.getQrCode()+"】不符合MES校验规则！");
            }
        }
        return craftStepDetail;
    }

    /**
     * 开始校验条码
     * @param stepDetail
     */
    private boolean checkCraftScanRule(CraftStepDetail stepDetail,String qrCode) {

        CraftScanRule craftScanRule = new CraftScanRule();
        craftScanRule.setStepId(stepDetail.getId());
        CraftScanRule scanRule = craftScanRuleMapper.selectOne(new QueryWrapper<>(craftScanRule));
        if(scanRule == null){
            throw new RuntimeException("当前工步【"+stepDetail.getStepCode()+"】没有配置扫码规则，请配置后再试！");
        }
        //如果不校验
        if(DictValueEnum.NO.getValue().equals(scanRule.getIsCheck())){
            return true;
        }
        String verifyStr = "";
        //是否全值匹配
        if(DictValueEnum.YES.getValue().equals(scanRule.getUpAllCheck())){
            verifyStr = qrCode;
        } else {
            //开始位置
            Integer startSite = scanRule.getUpBeginIndex() ;
            //结束位置
            Integer endSite = scanRule.getUpEndIndex();

            if(endSite > qrCode.length()){
                endSite = qrCode.length();
            }
            if(startSite > qrCode.length()){
                startSite = qrCode.length();
            }
            //判断字符串
            verifyStr = qrCode.substring(startSite - 1, endSite);
        }

        boolean res = false;

        if(DictValueEnum.VERIFY_TYPE_GD.getValue().equals(scanRule.getRuleType())){
            //（固定规则）
            res = verifyFixed(verifyStr, scanRule);
        }else if(DictValueEnum.VERIFY_TYPE_ZZ.getValue().equals(scanRule.getRuleType())){
            //（正则规则）
            res = verifyRegular(verifyStr, scanRule);
        }else if(DictValueEnum.VERIFY_TYPE_ZZS.getValue().equals(scanRule.getRuleType())){
            //（简单正则）
            res = verifySimpleRegular(verifyStr, scanRule);
        }

        if(res == true){
            //如果规则校验通过后判断是否需要校验长度
            return verifyLength(qrCode, scanRule);
        }

        return res;
    }



    /**
     * 获取参数详情
     * @param productCode
     * @param opCode
     * @return
     */
    private List<CraftParamDetail> getCraftParamDetail(String productCode,String opCode) {
        //获取配方
        CraftParam craftParam = getCraftParam(productCode);
        if(craftParam == null){
            return new ArrayList<>();
        }
        CraftParamDetail craftParamDetail = new CraftParamDetail();
        craftParamDetail.setParamsId(craftParam.getId());
        craftParamDetail.setOperationCode(opCode);
        return craftParamDetailMapper.selectList(new QueryWrapper<>(craftParamDetail).orderByAsc("param_code","param_name"));
    }

    /**
     * 获取工艺参数
     * @param productCode
     * @return
     */
    private CraftParam getCraftParam(String productCode) {
        CraftParam craftParam = new CraftParam();
        craftParam.setProductCode(productCode);
        craftParam.setIsDisable(DictValueEnum.START.getValue());
        return craftParamMapper.selectOne(new QueryWrapper<>(craftParam));
    }

    /**
     * 获取工艺工步
     * @param productCode
     * @return
     */
    private CraftStep getCraftStep(String productCode) {
        CraftStep craftStep = new CraftStep();
        craftStep.setProductCode(productCode);
        craftStep.setIsDisable(DictValueEnum.START.getValue());
        return craftStepMapper.selectOne(new QueryWrapper<>(craftStep));
    }

    /**
     * 获取工步详情
     * @param productCode
     * @param opCode
     * @return
     */
    private List<CraftStepDetail> getCraftStepDetail(String productCode, String opCode) {
        CraftStep craftStep = getCraftStep(productCode);
        if(craftStep == null){
            return new ArrayList<>();
        }
        CraftStepDetail craftStepDetail = new CraftStepDetail();
        craftStepDetail.setStepsId(craftStep.getId());
        craftStepDetail.setOperationCode(opCode);
        QueryWrapper<CraftStepDetail> wrapper = new QueryWrapper<>(craftStepDetail).orderByAsc("step_no");
        return craftStepDetailMapper.selectList(wrapper);
    }

    /**
     * 获取工步详情
     * @param productCode
     * @param opCode
     * @param stepCode
     * @return
     */
    private CraftStepDetail getCraftStepDetail(String productCode, String opCode, String stepCode) {
        CraftStep craftStep = getCraftStep(productCode);
        if(craftStep == null){
            throw new RuntimeException("当前产品【"+productCode+"】未配置或者未启用有对应的工步，请配置正确的工步后再生产！");
        }
        CraftStepDetail craftStepDetail = new CraftStepDetail();
        craftStepDetail.setStepsId(craftStep.getId());
        craftStepDetail.setOperationCode(opCode);
        craftStepDetail.setStepCode(stepCode);
        CraftStepDetail stepDetail = craftStepDetailMapper.selectOne(new QueryWrapper<>(craftStepDetail));
        if(stepDetail == null){
            throw new RuntimeException("获取工步【"+stepCode+"】失败，该工步编号不存在或者已删除！请检查后再试！");
        }
        return stepDetail;
    }


    /**
     * 获取当前产品型号
     * @param productCode
     * @return
     */
    private ProductModel getProductModel(String productCode) {
        ProductModel productModel = new ProductModel();
        productModel.setProductCode(productCode);
        ProductModel model = productModelMapper.selectOne(new QueryWrapper<>(productModel));
        if(model == null){
            throw new RuntimeException("当前产品型号【"+productCode+"】没有在MES系统中维护，请维护后再试！");
        }
        return model;
    }


    /**
     * 获取程序号
     * @param productCode
     * @param operationCode
     * @return
     */
    private CraftProductProgram getProductProgram(String productCode, String operationCode) {
        CraftProductProgram craftProductProgram = new CraftProductProgram();
        craftProductProgram.setProductCode(productCode);
        craftProductProgram.setOperationCode(operationCode);
        CraftProductProgram program = craftProductProgramMapper.selectOne(new QueryWrapper<>(craftProductProgram));
        if(program == null){
            throw new RuntimeException("当前产品型号【"+productCode+"】【"+operationCode+"】没有配置程序号，请配置后再试！");
        }
        return program;
    }


    /**
     * 校验固定内容
     * @return
     */
    public boolean verifyFixed(String verifyStr,CraftScanRule scanRule){
        try {
            if(verifyStr.equals(scanRule.getRuleContent())){
                return true;
            }
            return false;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 简易正则
     * @param verifyStr
     * @param scanRule
     * @return
     */
    public boolean verifySimpleRegular(String verifyStr,CraftScanRule scanRule) {
        try {
            if(scanRule.getRuleContent().length() != verifyStr.length()){
                return false;
            }
            String[] upArr = verifyStr.split("");

            String[] ruleArr = scanRule.getRuleContent().split("");

            for (int i = 0; i < ruleArr.length; i++) {
                String rule = ruleArr[i];
                if(!rule.equals("*") && !upArr[i].equals(rule)){
                    return false;
                }
            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 正则校验
     * @param verifyStr
     * @param scanRule
     * @return
     */
    public boolean verifyRegular(String verifyStr,CraftScanRule scanRule){
        try {
            //正则表达式
            return Pattern.matches(scanRule.getRuleContent(), verifyStr);
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 校验长度
     * @param qrCode
     * @param scanRule
     * @return
     */
    public boolean verifyLength(String qrCode,CraftScanRule scanRule){
        //条码长度(如果不等于0则判断上传的条码长度)
        Integer codeLength = scanRule.getUpLength();
        if(codeLength != null && codeLength != 0){
            int length = qrCode.length();
            if(codeLength != length){
                return false;
            }
        }
        return true;
    }
}

